/*
 * Copyright (C) 2009 Leandro Aparecido <lehphyro@gmail.com>
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.metalframework.jsf.navigation;

import static org.metalframework.util.AnnotationUtils.*;

import java.lang.reflect.*;
import java.util.*;

import org.apache.commons.lang.*;
import org.metalframework.annotation.*;

public class NavigationRuleParser {

	public List<NavigationRule> parse(Method method) {
		List<NavigationRule> rules = new ArrayList<NavigationRule>();
		Navigation.Rules navigations = getAnnotation(method, Navigation.Rules.class);
		
		if (navigations == null) {
			Navigation navigation = getAnnotation(method, Navigation.class);
			if (navigation != null) {
				rules.add(parse(navigation, method));
			}
		} else {
			boolean fallbackDetected = false;
			for (Navigation navigation : navigations.value()) {
				NavigationRule rule = parse(navigation, method);
				if (rule.isFallback()) {
					if (fallbackDetected) {
						throw new IllegalArgumentException("There can only be one fallback navigation rule in the method: " + method);
					}
					fallbackDetected = true;
				}
				rules.add(rule);
			}
		}
		
		return rules;
	}
	
	protected NavigationRule parse(Navigation navigation, Method method) {
		NavigationRule rule = new NavigationRule();
		
		rule.setViewId(parseViewId(navigation, method));
		rule.setWhen(parseWhen(navigation));
		rule.setWhenNull(navigation.whenNull());
		rule.setWhenNotNull(navigation.whenNotNull());
		rule.setOnThrowable(navigation.onException());
		rule.setRedirect(navigation.redirect());
		rule.setFallback(navigation.fallback());
		
		return rule;
	}

	protected String parseViewId(Navigation navigation, Method method) {
		String viewId;
		
		if (navigation.redisplay()) {
			if (navigation.redirect()) {
				throw new IllegalArgumentException("Method [" + method + "] cannot have both redisplay and redirect true in the same navigation rule");
			}
			if (StringUtils.isBlank(navigation.view())) {
				viewId = null;
			} else {
				throw new IllegalArgumentException("Method [" + method + "] cannot have both view and redisplay in the same navigation rule");
			}
		} else {
			if (StringUtils.isBlank(navigation.view())) {
				if (navigation.redirect()) {
					throw new IllegalArgumentException("Method [" + method + "] cannot have redirect true with null view");
				}
				viewId = null;
			} else {
				viewId = navigation.view();
			}
		}
		
		return viewId;
	}
	
	protected String parseWhen(Navigation navigation) {
		if (StringUtils.isBlank(navigation.when())) {
			return null;
		}
		
		return navigation.when();
	}

}
